﻿using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Collections;
using MyRemotingEnvironment;
using ServerServices;
using CommonLib;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Configuration;

namespace PadiCalServer
{
    class PadiCalServer
    {

        private int id;

        private static PServer selfInstance;

        private ServerService serverServ;


        static void Main(string[] args)
        {
            PadiCalServer a = new PadiCalServer(Convert.ToInt16(args[0]), args[2]);
/*
            Thread trd1 = new Thread(new ThreadStart(monitorPrimaryServer));
            trd1.IsBackground = true;
            trd1.Start();*/

            Thread trd2 = new Thread(new ThreadStart(sincronizeData));
            trd2.IsBackground = true;
            trd2.Start();

            Console.ReadLine();
            
        }

        public PadiCalServer(int idnum, String port)
        {

            id = idnum;
            /* Tirei esta parte da net para conseguir obter o ip do client */
            BinaryServerFormatterSinkProvider bp = new BinaryServerFormatterSinkProvider();
            ClientIPServerSinkProvider csp = new ClientIPServerSinkProvider();
            csp.Next = bp;
            Hashtable ht = new Hashtable();
            ht.Add("port", port);
            TcpChannel channel = new TcpChannel(ht, null, csp);
            ChannelServices.RegisterChannel(channel, false);

            Console.WriteLine("PadiCalServer {0} iniciado... Port {1}", id, port);

            this.serverServ = new ServerService();

            ObjRef serviceProxy = RemotingServices.Marshal(this.serverServ, "ServerService");

            IPAddress myIpv4Address = Array.FindLast(
                        Dns.GetHostEntry(string.Empty).AddressList,
                        a => a.AddressFamily == AddressFamily.InterNetwork);

            //cria uma referencia deste server
            selfInstance = new PServer(idnum, myIpv4Address, Convert.ToInt32(port));

            IServerService servicos = (IServerService)Activator.GetObject(typeof(IServerService),
            "tcp://" + myIpv4Address.ToString() + ":" + port + "/ServerService");

            servicos.setThisServer(selfInstance);
            setKnownServers(idnum, servicos);

            servicos.determineThePrimary();
            
           // servicos.printKnowingServers();

        }
 


      /**
       * Este método adiciona os servidores à lista de servidores conhecidos para cada processo PadiCliente 
       * em função do idNum
       **/
        public static void setKnownServers(int serverId, IServerService servicos)
        {
            string serverAddress1 = ConfigurationSettings.AppSettings["central-1"];
            string serverAddress2 = ConfigurationSettings.AppSettings["central-2"];
            string serverAddress3 = ConfigurationSettings.AppSettings["central-3"];

            if (serverId == 1)
            {
                string serverIp2 = serverAddress2.Substring(0, 9);
                string serverIp2Port = serverAddress2.Substring(10, 4);

                string serverIp3 = serverAddress3.Substring(0, 9);
                string serverIp3Port = serverAddress3.Substring(10, 4);

                PServer server2 = new PServer(2, IPAddress.Parse(serverIp2), Convert.ToInt32(serverIp2Port));
                PServer server3 = new PServer(3, IPAddress.Parse(serverIp3), Convert.ToInt32(serverIp3Port));

                servicos.addNewServer(server2);
                servicos.addNewServer(server3);

            }

            if (serverId == 2)
            {
                string serverIp1 = serverAddress1.Substring(0, 9);
                string serverIp1Port = serverAddress1.Substring(10, 4);

                string serverIp3 = serverAddress3.Substring(0, 9);
                string serverIp3Port = serverAddress3.Substring(10, 4);

                PServer server1 = new PServer(1, IPAddress.Parse(serverIp1), Convert.ToInt32(serverIp1Port));
                PServer server3 = new PServer(3, IPAddress.Parse(serverIp3), Convert.ToInt32(serverIp3Port));

                servicos.addNewServer(server1);
                servicos.addNewServer(server3);

            }

            if (serverId == 3)
            {
                string serverIp1 = serverAddress1.Substring(0, 9);
                string serverIp1Port = serverAddress1.Substring(10, 4);

                string serverIp2 = serverAddress2.Substring(0, 9);
                string serverIp2Port = serverAddress2.Substring(10, 4);

                PServer server1 = new PServer(1, IPAddress.Parse(serverIp1), Convert.ToInt32(serverIp1Port));
                PServer server2 = new PServer(2, IPAddress.Parse(serverIp2), Convert.ToInt32(serverIp2Port));

                servicos.addNewServer(server1);
                servicos.addNewServer(server2);



            }

        }

         /**
          * Este método adiciona os servidores à lista de servidores conhecidos para cada processo PadiCliente 
          * em função do idNum
          **/
        public static void setKnownServers(int serverId, int offlineServerId,IServerService servicos)
        {
            string serverAddress1 = ConfigurationSettings.AppSettings["central-1"];
            string serverAddress2 = ConfigurationSettings.AppSettings["central-2"];
            string serverAddress3 = ConfigurationSettings.AppSettings["central-3"];

            if (serverId == 1)
            {
                if (offlineServerId != 2)
                {
                    string serverIp2 = serverAddress2.Substring(0, 9);
                    string serverIp2Port = serverAddress2.Substring(10, 4);
                    PServer server2 = new PServer(2, IPAddress.Parse(serverIp2), Convert.ToInt32(serverIp2Port));
                    servicos.addNewServer(server2);
                }

                if (offlineServerId != 3)
                {
                    string serverIp3 = serverAddress3.Substring(0, 9);
                    string serverIp3Port = serverAddress3.Substring(10, 4);
                    PServer server3 = new PServer(3, IPAddress.Parse(serverIp3), Convert.ToInt32(serverIp3Port));
                    servicos.addNewServer(server3);
                }

            }

            if (serverId == 2)
            {
              
                if (offlineServerId != 1)
                {
                    string serverIp1 = serverAddress1.Substring(0, 9);
                    string serverIp1Port = serverAddress1.Substring(10, 4);
                    PServer server1 = new PServer(1, IPAddress.Parse(serverIp1), Convert.ToInt32(serverIp1Port));
                    servicos.addNewServer(server1);
                }

                if (offlineServerId != 3)
                {
                    string serverIp3 = serverAddress3.Substring(0, 9);
                    string serverIp3Port = serverAddress3.Substring(10, 4);
                    PServer server3 = new PServer(3, IPAddress.Parse(serverIp3), Convert.ToInt32(serverIp3Port));
                    servicos.addNewServer(server3);
                }

            }

            if (serverId == 3)
            {

                if (offlineServerId != 1)
                {
                    string serverIp1 = serverAddress1.Substring(0, 9);
                    string serverIp1Port = serverAddress1.Substring(10, 4);
                    PServer server1 = new PServer(1, IPAddress.Parse(serverIp1), Convert.ToInt32(serverIp1Port));
                    servicos.addNewServer(server1);
                }

                if (offlineServerId != 2)
                {
                    string serverIp2 = serverAddress2.Substring(0, 9);
                    string serverIp2Port = serverAddress2.Substring(10, 4);
                    PServer server2 = new PServer(2, IPAddress.Parse(serverIp2), Convert.ToInt32(serverIp2Port));
                    servicos.addNewServer(server2);
                }
            }

        }

       /**
        * Faz a monitoria ao primary server para ver se está online/offline
        *  se estiver offline redefine os processos online, para que se 
        *  determine o novo server
        **/
        private static void monitorPrimaryServer()
        {

            IServerService servicos = (IServerService)Activator.GetObject(typeof(IServerService),
            "tcp://" + selfInstance.Ip.ToString() + ":" + selfInstance.Port + "/ServerService");
            Console.WriteLine("<< monitor >> tcp://" + selfInstance.Ip.ToString() + ":" + selfInstance.Port + "/ServerService");
            while (true)
            {
                Thread.Sleep(10000);// verifica o estado do primary server a cada 10segundos
                if (servicos.getPrimaryServer().Status==0)
                {
                    servicos.determineThePrimary();
                }
            }
        }

        /**
         *  Este método realiza a replicacao de dados do primary server periodicamente 
         *  se este processo for o primary
         **/
        private static void sincronizeData()
        {

            IServerService servicos = (IServerService)Activator.GetObject(typeof(IServerService),
               "tcp://" + selfInstance.Ip.ToString() + ":" + selfInstance.Port + "/ServerService");

            while (true)
            {
                if (selfInstance.Role == 1)
                {
                    servicos.replicate();
                    Console.WriteLine("Data sincronized...sleeping");
                    Thread.Sleep(5000);//5segundos
                }
            }

        }
    }
    
}
